package org.deltaset.meta.faces;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponentBase;
import javax.faces.convert.Converter;
import org.deltaset.faces.PageHelper;
import org.deltaset.faces.component.SelectOption;
import org.deltaset.meta.Catalog;
import org.deltaset.meta.CollectionHandler;
import org.deltaset.meta.EntityResolver;
import org.deltaset.meta.ExecutorResult;
import org.deltaset.meta.Finder;
import org.deltaset.meta.LabelResolver;
import org.deltaset.meta.MetaAction;
import org.deltaset.meta.MetaActionExecutor;
import org.deltaset.meta.MetaActionHandler;
import org.deltaset.meta.MetaEntry;
import org.deltaset.meta.MetaField;
import org.deltaset.meta.MetaHandlerFactory;
import org.deltaset.meta.MetaHandlerFactoryBuilder;
import org.deltaset.meta.OptionBuilder;
import org.deltaset.meta.extension.RepositoryProvider;

/**
 *
 * @author dmakariev
 */
public class MetaCrudNamingContainer extends UIComponentBase implements NamingContainer {

    private static final Logger logger = Logger.getLogger(MetaCrudNamingContainer.class.getName());
    private final FacesStateRepository stateRepository;
    private PageHelper<Object> all;
    private LazyInit lazyInit;
    /**
     * <p>The standard component type for this component.</p>
     */
    public static final String COMPONENT_TYPE = "org.deltaset.metaCrud";
    /**
     * <p>The standard component family for this component.</p>
     */
    public static final String COMPONENT_FAMILY = "javax.faces.NamingContainer";

    public MetaCrudNamingContainer() {
        this.stateRepository = new FacesStateRepository(this);
        logger.log(Level.INFO, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ MetaCrudNamingContainer CONSTRUCTOR !!!!!!!!!!!!!!!! ");
    }

    @Override
    public String getFamily() {
        return "javax.faces.NamingContainer";
    }

    public MetaCrudNamingContainer getMeta() {
        return this;
    }

    ///////////////
    public String getLabelEntity() {
        final Class entityClass = getMetaEntry().getEntityClass();
        return getLabelResolver().labelClass(entityClass, stateRepository.getCurrentLocale());
    }

    public String labelClass(final Class entityClass) {
        return getLabelResolver().labelClass(entityClass, stateRepository.getCurrentLocale());
    }

    public String labelAction(final MetaAction action) {
        return getLabelResolver().labelAction(action, stateRepository.getCurrentLocale());
    }

    public String labelField(final MetaField field, final Object owner) {
        return getLabelResolver().labelField(field, owner, stateRepository.getCurrentLocale());
    }
////////////////////

    public List<MetaAction> getOtherActions() {
        return getMetaEntry().getOtherActions();
    }

    public List<MetaAction> getOtherContextActions() {
        return getMetaEntry().getOtherContextActions();
    }

    public List<MetaAction> getFinderActions() {
        return getMetaEntry().getFinderActions();
    }

    public List<MetaAction> getFactoryActions() {
        return getMetaEntry().getFactoryActions();
    }

    public List<MetaAction> getContextActions() {
        return getMetaEntry().getContextActions();
    }

    public List<MetaField> getListFields() {
        return getMetaEntry().getListFields();
    }

    public List<MetaField> getDetailFields() {
        return getMetaEntry().getDetailFields();
    }

    public List<MetaField> getFormFields() {
        return getMetaEntry().getFormFields();
    }

    public boolean isDisabled(final MetaAction metaAction, final Object model) {
        return getMetaActionHandler().isDisabled(metaAction, model);
    }

    public boolean isHidden(final MetaAction metaAction, final Object model) {
        return getMetaActionHandler().isHidden(metaAction, model);
    }

    public SelectOption[] getSelectOptions(final MetaField metaField) {
        final List<SelectOption> list = getOptionBuilder().getOptions(metaField);
        final SelectOption[] result = new SelectOption[list.size()];
        list.toArray(result);
        return result;
    }

    private OptionBuilder<SelectOption> getOptionBuilder() {
        return getLazyInit().getOptionBuilder();
    }

    public Converter getGenericEntityConverter() {
        return getLazyInit().getGenericEntityConverter();
    }
    /////////////////////

    public boolean getHasValidationErrors() {
        return getMetaCrud().getHasValidationErrors();
    }

    public String getCurrentViewTemplate() {
        return getMetaCrud().getCurrentViewTemplate();
    }

    public void showDetail() {
        getMetaCrud().showDetail();
    }

    public void showList() {
        getMetaCrud().showList();

    }

    public String templateSrc(final String catalogModifier, final MetaField metaField) {
        final String src = getLazyInit().getCatalog().templateSrc(catalogModifier, metaField);
        return src;
    }

    public void forceChangeTo(Object item) {
        getMetaCrud().forceChangeTo(item);
        this.all = null;
    }

    /////////////////////    
    public Object getModel() {
        return getMetaCrud().getModel();
    }

    public void setModel(final Object model) {
        getMetaCrud().setModel(model);
    }

    public boolean loadModel(final String id) {
        return getMetaCrud().loadModel(id);
    }

    public String getModelId(final Object model) {
        return getMetaActionHandler().getModelId(model);
    }

    //TODO: is this used ?.. should be replaced  by disableXXX infrastructure ?
    public <T> boolean isNew(final T entity) {
        return getMetaActionHandler().isNew(entity);
    }

    public String modelTitle(Object model) {
        return getMetaActionHandler().modelTitle(model);
    }

    public String getClassName() {
        return getMetaEntry().getEntityClass().getName();
    }

    public Class getModelClass() {
        return getMetaEntry().getEntityClass();
    }
////////////////

    public void cancelExecutor() {
        getMetaCrud().cancelExecutor();
    }

    public void loadExecutor(final MetaAction metaAction, final Object model) {
        this.all = null;
        getMetaCrud().loadExecutor(metaAction, model);
    }

    public void executeAction() {
        this.all = null;
        getMetaCrud().executeAction();
    }

    public MetaActionExecutor getExecutor() {
        return getMetaCrud().getExecutor();
    }

    public ExecutorResult getExecutorResult() {
        return getMetaCrud().getExecutorResult();
    }
////////////////

    public boolean getShowFinderForm() {
        return getMetaCrud().getShowFinderForm();
    }

    public MetaActionExecutor getFinder() {
        return getMetaCrud().getFinder();
    }

    public void cancelFinder() {
        getMetaCrud().cancelFinder();
    }
    
    public void previousView() {
        getMetaCrud().previousView();
    }
    
    public boolean getDisabledPreviousView(){
        return getMetaCrud().getDisabledPreviousView();
    }

    public void executeFinder() {
        getMetaCrud().executeFinder();
    }

    public void loadFinder(final MetaAction metaAction) {
        getMetaCrud().loadFinder(metaAction);
    }

    public PageHelper<Object> all(final String pageNum) {

        final boolean isPageNumEmpty = null == pageNum || "".equals(pageNum);
        final String currentPageNum = (true == isPageNumEmpty) ? "1" : pageNum;
        if (null == all) {
            final Finder allFinder = getMetaCrud().allFinder(currentPageNum);
            all = fromFinder(allFinder).load(currentPageNum);
        }
        return all;
    }

    private static PageHelper fromFinder(final Finder finder) {
        final PageHelper ph = PageHelper.newInstance(new PageHelper.Finder<Object>() {

            @Override
            public long counter(Object... params) {
                return finder.counter(params);
            }

            @Override
            public List<Object> finder(int firstResult, int maxResult, Object... params) {
                return finder.finder(firstResult, maxResult, params);
            }
        });
        return ph;
    }

//////////////
    EntityResolver getEntityResolver() {
        return getLazyInit().getEntityResolver();
    }

    CollectionHandler getCollectionHandler() {
        return getLazyInit().getCollectionHandler();
    }

    private MetaCrud getMetaCrud() {
        return getLazyInit().getMetaCrud();
    }

    private MetaEntry getMetaEntry() {
        return getMetaCrud().getMetaEntry();
    }

    private MetaActionHandler getMetaActionHandler() {
        return getLazyInit().getMetaActionHandler();
    }

    private LabelResolver getLabelResolver() {
        return getLazyInit().getLabelResolver();
    }

    private LazyInit getLazyInit() {
        if (null == lazyInit) {
            lazyInit = new LazyInit(stateRepository);
        }
        return lazyInit;
    }

    private static class LazyInit {

        private static final String DEFAULT_TEMPLATE_CATALOG_NAME = "META-INF/deltaset/dsTemplateCatalog.properties";
        private static final String DEFAULT_USUPPORTED_TEMPLATE = "/META-INF/resources/deltaset.meta/internal/fields/unsupported.xhtml";
        private final MetaHandlerFactory metaHandlerFactory;
        private final LabelResolver labelResolver;
        private final Catalog catalog;
        private final EntityResolver entityResolver;
        private final CollectionHandler collectionHandler;
        private final MetaActionHandler metaActionHandler;
        private final OptionBuilder<SelectOption> optionBuilder;
        private final GenericEntityConverter genericEntityConverter;
        private final MetaCrud metaCrud;

        LazyInit(final FacesStateRepository stateRepository) {
            final String catalogName = stateRepository.getValueCalatogName();
            final String metaTypeValue = stateRepository.getAttributeOrException("metaType");
            final RepositoryProvider repositoryProvider = stateRepository.getValueRepositoryProvider();

            this.metaHandlerFactory = buildMetaHandlerFactory(metaTypeValue, repositoryProvider);
            this.labelResolver = metaHandlerFactory.createLabelResolver();
            this.catalog = metaHandlerFactory.createCatalog(catalogName);
            this.entityResolver = metaHandlerFactory.createEntityResolver();
            this.collectionHandler = metaHandlerFactory.createCollectionHandler();
            this.metaActionHandler = metaHandlerFactory.createMetaActionHandler();
            this.optionBuilder = metaHandlerFactory.createOptionBuilder();
            this.genericEntityConverter = buildGenericEntityConverter(entityResolver);
            this.metaCrud = new MetaCrud(stateRepository, metaHandlerFactory, metaActionHandler);
        }

        private static MetaHandlerFactory buildMetaHandlerFactory(final String metaTypeValue, final RepositoryProvider repositoryProvider) {
            final MetaHandlerFactoryBuilder builder = MetaHandlerFactoryBuilder.newBuilder();
            builder.catalog(DEFAULT_TEMPLATE_CATALOG_NAME, DEFAULT_USUPPORTED_TEMPLATE, metaTypeValue);
            builder.provider(new CachingMetaHandlerProvider());
            builder.provider(new OptionBuilderProviderImpl());
            builder.provider(repositoryProvider);
            final MetaHandlerFactory metaHandlerFactory = builder.build();
            return metaHandlerFactory;
        }

        private static GenericEntityConverter buildGenericEntityConverter(final EntityResolver entityResolver) {
            final GenericEntityConverter genericEntityConverter = new GenericEntityConverter();
            genericEntityConverter.setEntityResolver(entityResolver);
            return genericEntityConverter;
        }

        public Catalog getCatalog() {
            return catalog;
        }

        public CollectionHandler getCollectionHandler() {
            return collectionHandler;
        }

        public EntityResolver getEntityResolver() {
            return entityResolver;
        }

        public LabelResolver getLabelResolver() {
            return labelResolver;
        }

        public MetaActionHandler getMetaActionHandler() {
            return metaActionHandler;
        }

        public MetaHandlerFactory getMetaHandlerFactory() {
            return metaHandlerFactory;
        }

        public OptionBuilder<SelectOption> getOptionBuilder() {
            return optionBuilder;
        }

        public GenericEntityConverter getGenericEntityConverter() {
            return genericEntityConverter;
        }

        public MetaCrud getMetaCrud() {
            return metaCrud;
        }
    }
}
